#pragma warning disable IDE1006 // Naming Styles


namespace Vanara.PInvoke;

/// <summary>Items from the Usp10.dll.</summary>
public static partial class Usp10
{
	/// <summary>
	/// SCRIPT_UNDEFINED: This is the only public script ordinal. May be forced into the eScript field of a SCRIPT_ANALYSIS to disable
	/// shaping. SCRIPT_UNDEFINED is supported by all fonts - ScriptShape will display whatever glyph is defined in the font CMAP table,
	/// or, if none, the missing glyph.
	/// </summary>
	public const ushort SCRIPT_UNDEFINED = 0;

	/// <summary>Substitution type. This member is normally set by ScriptRecordDigitSubstitution.</summary>
	[PInvokeData("usp10.h", MSDNShortId = "NS:usp10.tag_SCRIPT_DIGITSUBSTITUTE")]
	public enum SCRIPT_DIGITSUB : byte
	{
		/// <summary>
		/// SCRIPT_DIGITSUBSTITUTE_CONTEXT: Digits U+0030 - U+0039 will be substituted according to the language of prior letters. Before
		/// any letters, digits will be substituted according to the TraditionalDigitLangauge field of the SCRIPT_DIGIT_SUBSTITUTE
		/// structure. This field is normally set to the primary language of the Locale passed to ScriptRecordDigitSubstitution.
		/// </summary>
		SCRIPT_DIGITSUBSTITUTE_CONTEXT = 0,

		/// <summary>
		/// SCRIPT_DIGITSUBSTITUTE_NONE: Digits will not be substituted. Unicode values U+0030 to U+0039 will be displayed with Arabic (i.e.
		/// Western) numerals.
		/// </summary>
		SCRIPT_DIGITSUBSTITUTE_NONE = 1,

		/// <summary>
		/// SCRIPT_DIGITSUBSTITUTE_NATIONAL: Digits U+0030 - U+0039 will be substituted according to the NationalDigitLangauge field of
		/// the SCRIPT_DIGIT_SUBSTITUTE structure. This field is normally set to the national digits returned for the NLS LCTYPE
		/// LOCALE_SNATIVEDIGITS by ScriptRecordDigitSubstitution.
		/// </summary>
		SCRIPT_DIGITSUBSTITUTE_NATIONAL = 2,

		/// <summary>
		/// SCRIPT_DIGITSUBSTITUTE_TRADITIONAL: Digits U+0030 - U+0039 will be substituted according to the TraditionalDigitLangauge
		/// field of the SCRIPT_DIGIT_SUBSTITUTE structure. This field is normally set to the primary language of the Locale passed to ScriptRecordDigitSubstitution.
		/// </summary>
		SCRIPT_DIGITSUBSTITUTE_TRADITIONAL = 3,
	}

	/// <summary>Defines glyph characteristic information that an application needs to implement justification.</summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/usp10/ne-usp10-script_justify typedef enum tag_SCRIPT_JUSTIFY {
	// SCRIPT_JUSTIFY_NONE = 0, SCRIPT_JUSTIFY_ARABIC_BLANK = 1, SCRIPT_JUSTIFY_CHARACTER = 2, SCRIPT_JUSTIFY_RESERVED1 = 3,
	// SCRIPT_JUSTIFY_BLANK = 4, SCRIPT_JUSTIFY_RESERVED2 = 5, SCRIPT_JUSTIFY_RESERVED3 = 6, SCRIPT_JUSTIFY_ARABIC_NORMAL = 7,
	// SCRIPT_JUSTIFY_ARABIC_KASHIDA = 8, SCRIPT_JUSTIFY_ARABIC_ALEF = 9, SCRIPT_JUSTIFY_ARABIC_HA = 10, SCRIPT_JUSTIFY_ARABIC_RA = 11,
	// SCRIPT_JUSTIFY_ARABIC_BA = 12, SCRIPT_JUSTIFY_ARABIC_BARA = 13, SCRIPT_JUSTIFY_ARABIC_SEEN = 14, SCRIPT_JUSTIFY_ARABIC_SEEN_M = 15
	// } SCRIPT_JUSTIFY;
	[PInvokeData("usp10.h", MSDNShortId = "NE:usp10.tag_SCRIPT_JUSTIFY")]
	public enum SCRIPT_JUSTIFY : ushort
	{
		/// <summary>Justification cannot be applied at the glyph.</summary>
		SCRIPT_JUSTIFY_NONE,

		/// <summary>The glyph represents a blank in an Arabic run.</summary>
		SCRIPT_JUSTIFY_ARABIC_BLANK = 1,

		/// <summary>An inter-character justification point follows the glyph.</summary>
		SCRIPT_JUSTIFY_CHARACTER = 2,

		/// <summary>Reserved.</summary>
		SCRIPT_JUSTIFY_RESERVED1 = 3,

		/// <summary>The glyph represents a blank outside an Arabic run.</summary>
		SCRIPT_JUSTIFY_BLANK = 4,

		/// <summary>Reserved.</summary>
		SCRIPT_JUSTIFY_RESERVED2 = 5,

		/// <summary>Reserved.</summary>
		SCRIPT_JUSTIFY_RESERVED3 = 6,

		/// <summary>Normal middle-of-word glyph that connects to the right (begin).</summary>
		SCRIPT_JUSTIFY_ARABIC_NORMAL = 7,

		/// <summary>Kashida (U+0640) in the middle of the word.</summary>
		SCRIPT_JUSTIFY_ARABIC_KASHIDA = 8,

		/// <summary>Final form of an alef-like (U+0627, U+0625, U+0623, U+0622).</summary>
		SCRIPT_JUSTIFY_ARABIC_ALEF = 9,

		/// <summary>Final form of Ha (U+0647).</summary>
		SCRIPT_JUSTIFY_ARABIC_HA = 10,

		/// <summary>Final form of Ra (U+0631).</summary>
		SCRIPT_JUSTIFY_ARABIC_RA = 11,

		/// <summary>Final form of Ba (U+0628).</summary>
		SCRIPT_JUSTIFY_ARABIC_BA = 12,

		/// <summary>Ligature of alike (U+0628,U+0631).</summary>
		SCRIPT_JUSTIFY_ARABIC_BARA = 13,

		/// <summary>Highest priority: initial shape of Seen class (U+0633).</summary>
		SCRIPT_JUSTIFY_ARABIC_SEEN = 14,

		/// <summary>Highest priority: medial shape of Seen class (U+0633).</summary>
		SCRIPT_JUSTIFY_ARABIC_SEEN_M = 15
	}

	/// <summary>
	/// Flags specifying any special handling of the glyphs. By default, the glyphs are provided in logical order with no special handling.
	/// </summary>
	[PInvokeData("usp10.h", MSDNShortId = "NF:usp10.ScriptGetCMap")]
	[Flags]
	public enum SGCM : uint
	{
		/// <summary>
		/// The glyph array indicated by <c>pwOutGlyphs</c> should contain mirrored glyphs for those glyphs that have a mirrored equivalent.
		/// </summary>
		SGCM_RTL = 0x00000001
	}

	/// <summary>Flags specifying testing details.</summary>
	[Flags]
	public enum SIC : uint
	{
		/// <summary>Treat complex script letters as complex. This flag should normally be set.</summary>
		SIC_COMPLEX = 1,

		/// <summary>
		/// Treat digits U+0030 to U+0039 as complex. The application sets this flag if the string is displayed with digit substitution
		/// enabled. If the application is following the user's National Language Support (NLS) settings using the
		/// ScriptRecordDigitSubstitution function, it can pass a SCRIPT_DIGITSUBSTITUTE structure with the <c>DigitSubstitute</c> member
		/// set to SCRIPT_DIGITSUBSTITUTE_NONE.
		/// </summary>
		SIC_ASCIIDIGIT = 2,

		/// <summary>Treat neutrals as complex. The application sets this flag to display the string with right-to-left reading order.</summary>
		SIC_NEUTRAL = 4,
	}

	/// <summary>Flags indicating the analysis that is required.</summary>
	[PInvokeData("usp10.h", MSDNShortId = "NF:usp10.ScriptShapeOpenType")]
	[Flags]
	public enum SSA : uint
	{
		/// <summary>Retrieve break flags, that is, character and word stops.</summary>
		SSA_BREAK = 0x00000040,

		/// <summary>Clip the string at <c>iReqWidth.</c></summary>
		SSA_CLIP = 0x00000004,

		/// <summary>Internal - Used only by GDI during metafiling - Use ExtTextOutA for positioning</summary>
		SSA_DONTGLYPH = 0x40000000,

		/// <summary>Provide representation glyphs for control characters.</summary>
		SSA_DZWG = 0x00000010,

		/// <summary>Use fallback fonts.</summary>
		SSA_FALLBACK = 0x00000020,

		/// <summary>Justify the string to <c>iReqWidth</c>.</summary>
		SSA_FIT = 0x00000008,

		/// <summary>Internal - calculate full width and out the number of chars can fit in iReqWidth.</summary>
		SSA_FULLMEASURE = 0x04000000,

		/// <summary>Retrieve missing glyphs and <c>pwLogClust</c> with GetCharacterPlacement conventions.</summary>
		SSA_GCP = 0x00000200,

		/// <summary>Generate glyphs, positions, and attributes.</summary>
		SSA_GLYPHS = 0x00000080,

		/// <summary>Remove the first "&amp;" from displayed string.</summary>
		SSA_HIDEHOTKEY = 0x00002000,

		/// <summary>Replace "&amp;" with underline on subsequent code point.</summary>
		SSA_HOTKEY = 0x00000400,

		/// <summary>
		/// Display underline only. The resulting bit pattern might be displayed, using an XOR mask, to toggle the visibility of the
		/// hotkey underline without disturbing the text.
		/// </summary>
		SSA_HOTKEYONLY = 0x00002400,

		/// <summary>Internal - Used when DC is mirrored</summary>
		SSA_LAYOUTRTL = 0x20000000,

		/// <summary>Apply East Asian font linking and association to noncomplex text.</summary>
		SSA_LINK = 0x00001000,

		/// <summary>Internal - enable FallBack for all LPK Ansi calls Except BiDi hDC calls</summary>
		SSA_LPKANSIFALLBACK = 0x08000000,

		/// <summary>Write items with ExtTextOutW calls, not with glyphs.</summary>
		SSA_METAFILE = 0x00000800,

		/// <summary>Internal - Used by GCP to justify the non Arabic glyphs only.</summary>
		SSA_NOKASHIDA = 0x80000000,

		/// <summary>Duplicate input string containing a single character <c>cString</c> times.</summary>
		SSA_PASSWORD = 0x00000001,

		/// <summary>Internal</summary>
		SSA_PIDX = 0x10000000,

		/// <summary>Use base embedding level 1.</summary>
		SSA_RTL = 0x00000100,

		/// <summary>Expand tabs.</summary>
		SSA_TAB = 0x00000002,
	}

	/// <summary>Contains the x and y offsets of the combining glyph.</summary>
	/// <remarks>
	/// The members of this structure are named as they are so that they are not confused with the "dx" and "dy" designators for physical
	/// units in Uniscribe functions and structures.
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/usp10/ns-usp10-goffset typedef struct tagGOFFSET { LONG du; LONG dv; } GOFFSET;
	[PInvokeData("usp10.h", MSDNShortId = "NS:usp10.tagGOFFSET")]
	[StructLayout(LayoutKind.Sequential)]
	public struct GOFFSET
	{
		/// <summary>x offset, in logical units, for the combining glyph.</summary>
		public int du;

		/// <summary>y offset, in logical units, for the combining glyph.</summary>
		public int dv;
	}

	/// <summary>Contains information about a single OpenType feature to apply to a run.</summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/usp10/ns-usp10-opentype_feature_record typedef struct opentype_feature_record {
	// OPENTYPE_TAG tagFeature; LONG lParameter; } OPENTYPE_FEATURE_RECORD;
	[PInvokeData("usp10.h", MSDNShortId = "NS:usp10.opentype_feature_record")]
	[StructLayout(LayoutKind.Sequential)]
	public struct OPENTYPE_FEATURE_RECORD
	{
		/// <summary>
		/// OPENTYPE_TAG value containing a registered or private OpenType feature tag. For information on feature tags, see http://www.microsoft.com/typography/otspec/featuretags.htm.
		/// </summary>
		public uint tagFeature;

		/// <summary>
		/// <para>Value indicating how to apply the feature tag. Possible values are defined in the following table.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>0</term>
		/// <term>Feature is disabled and should not be applied.</term>
		/// </item>
		/// <item>
		/// <term>1</term>
		/// <term>Feature is active. If the feature offers several alternatives, select the first value.</term>
		/// </item>
		/// <item>
		/// <term>Greater than 1</term>
		/// <term>
		/// Feature is active. Select the alternative value at this index. Should be used only when multiple alternatives are available
		/// for a feature.
		/// </term>
		/// </item>
		/// </list>
		/// </summary>
		public int lParameter;
	}

	/// <summary>
	/// Defines a 4-byte array that contains four 8-bit ASCII values of space, A-Z, or a-z to identify OpenType script, language, and
	/// font feature tags.
	/// </summary>
	/// <remarks>
	/// <para>The following examples define representations of OpenType feature tags.</para>
	/// <list type="bullet">
	/// <item>
	/// <term>The feature tag for the ligature feature is "liga".</term>
	/// </item>
	/// <item>
	/// <term>
	/// The language tags for Romanian, Urdu, and Persian are "ROM ", "URD ", and "FAR ", respectively. Note that each of these tags ends
	/// with a space.
	/// </term>
	/// </item>
	/// <item>
	/// <term>The script tags for Latin and Arabic scripts are "latn" and "arab", respectively.</term>
	/// </item>
	/// </list>
	/// <para>For more information on OpenType feature tags and the OpenType specification, see <a href="https://www.microsoft.com/typography/otspec/featuretags.htm">https://www.microsoft.com/typography/otspec/featuretags.htm</a>.</para>
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/intl/opentype-tag
	[PInvokeData("usp10.h")]
	[StructLayout(LayoutKind.Sequential)]
	public struct OPENTYPE_TAG : IEquatable<OPENTYPE_TAG>
	{
		/// <summary>When all characters in an item are neutral.</summary>
		public const uint SCRIPT_TAG_UNKNOWN = 0x00000000;

		private readonly uint value;

		/// <summary>Creates an <see cref="OPENTYPE_TAG"/> from a <see cref="uint"/> value.</summary>
		public OPENTYPE_TAG(uint value) => this.value = value;

		/// <summary>Creates an <see cref="OPENTYPE_TAG"/> from four characters.</summary>
		/// <param name="a">The first character.</param>
		/// <param name="b">The second character.</param>
		/// <param name="c">The third character.</param>
		/// <param name="d">The fourth character.</param>
		public OPENTYPE_TAG(char a, char b, char c, char d) : this(new byte[] { (byte)a, (byte)b, (byte)c, (byte)d }) { }

		/// <summary>Creates an <see cref="OPENTYPE_TAG"/> from a four character string.</summary>
		/// <param name="tag">The four character string.</param>
		public OPENTYPE_TAG(string tag) : this(tag.GetBytes(false, CharSet.Ansi)) { }

		/// <summary>Creates an <see cref="OPENTYPE_TAG"/> from a four characters array.</summary>
		/// <param name="chars">The four character array.</param>
		public OPENTYPE_TAG(char[] chars) : this(Array.ConvertAll(chars, c => (byte)c)) { }

		/// <summary>Creates an <see cref="OPENTYPE_TAG"/> from a four byte array.</summary>
		/// <param name="bytes">The four byte array.</param>
		public OPENTYPE_TAG(byte[] bytes) : this(bytes.Length == 4 ? BitConverter.ToUInt32(bytes, 0) :
			throw new ArgumentOutOfRangeException(nameof(bytes), "Value must have only four elements.")) { }

		/// <summary>Performs an implicit conversion from <see cref="OPENTYPE_TAG"/> to <see cref="System.UInt32"/>.</summary>
		/// <param name="tag">The tag.</param>
		/// <returns>The result of the conversion.</returns>
		public static implicit operator uint(OPENTYPE_TAG tag) => tag.value;

		/// <summary>Performs an implicit conversion from <see cref="System.UInt32"/> to <see cref="OPENTYPE_TAG"/>.</summary>
		/// <param name="tag">The tag.</param>
		/// <returns>The result of the conversion.</returns>
		public static implicit operator OPENTYPE_TAG(uint tag) => new(tag);

		/// <summary>Performs an implicit conversion from <see cref="string"/> to <see cref="OPENTYPE_TAG"/>.</summary>
		/// <param name="tag">The tag.</param>
		/// <returns>The result of the conversion.</returns>
		public static implicit operator OPENTYPE_TAG(string tag) => new(tag);

		/// <summary>Determines whether the specified <see cref="System.Object"/>, is equal to this instance.</summary>
		/// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
		/// <returns>
		/// <see langword="true"/> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <see langword="false"/>.
		/// </returns>
		public override bool Equals(object? obj) => obj is OPENTYPE_TAG tAG && Equals(tAG);

		/// <summary>Determines whether the specified <see cref="OPENTYPE_TAG"/>, is equal to this instance.</summary>
		/// <param name="other">The <see cref="OPENTYPE_TAG"/> to compare with this instance.</param>
		/// <returns>
		/// <see langword="true"/> if the specified <see cref="OPENTYPE_TAG"/> is equal to this instance; otherwise, <see langword="false"/>.
		/// </returns>
		public bool Equals(OPENTYPE_TAG other) => value==other.value;

		/// <summary>Returns a hash code for this instance.</summary>
		/// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.</returns>
		public override int GetHashCode() => value.GetHashCode();

		/// <summary>Converts to string.</summary>
		/// <returns>A <see cref="string"/> that represents this instance.</returns>
		public override string ToString()
		{
			if (value == SCRIPT_TAG_UNKNOWN)
				return "Unknown";
			var bytes = BitConverter.GetBytes(value);
			return $"'{(char)bytes[0]}{(char)bytes[1]}{(char)bytes[2]}{(char)bytes[3]}'";
		}
	}

	/// <summary>Contains a portion of a Unicode string, that is, an "item".</summary>
	/// <remarks>
	/// <para>
	/// This structure is filled by ScriptItemize or ScriptItemizeOpenType, each of which breaks a Unicode string into individually
	/// shapeable items. Neither function accesses the <c>SCRIPT_ANALYSIS</c> structure directly. Each function handles an array of
	/// SCRIPT_ITEM structures, each of which has a member defining a <c>SCRIPT_ANALYSIS</c> structure.
	/// </para>
	/// <para>
	/// Applications that use ScriptItemizeOpenType instead of ScriptItemize should also use ScriptShapeOpenType and ScriptPlaceOpenType
	/// instead of ScriptShape and ScriptPlace. For more information, see Displaying Text with Uniscribe.
	/// </para>
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/usp10/ns-usp10-script_analysis typedef struct tag_SCRIPT_ANALYSIS { WORD
	// eScript : 10; WORD fRTL : 1; WORD fLayoutRTL : 1; WORD fLinkBefore : 1; WORD fLinkAfter : 1; WORD fLogicalOrder : 1; WORD
	// fNoGlyphIndex : 1; SCRIPT_STATE s; } SCRIPT_ANALYSIS;
	[PInvokeData("usp10.h", MSDNShortId = "NS:usp10.tag_SCRIPT_ANALYSIS")]
	[StructLayout(LayoutKind.Sequential)]
	public struct SCRIPT_ANALYSIS
	{
		private ushort bits;

		/// <summary>Initializes a new instance of the <see cref="SCRIPT_ANALYSIS"/> struct.</summary>
		/// <param name="bits">The bits.</param>
		/// <param name="state">The state.</param>
		public SCRIPT_ANALYSIS(ushort bits, SCRIPT_STATE state) => (this.bits, s) = (bits, state);

		/// <summary>
		/// <para>
		/// Opaque value identifying the engine that Uniscribe uses when calling the ScriptShape, ScriptPlace, and ScriptTextOut
		/// functions for the item. The value for this member is undefined and applications should not rely on its value being the same
		/// from one release to the next. An application can obtain the attributes of <c>eScript</c> by calling ScriptGetProperties.
		/// </para>
		/// <para>To disable shaping, the application should set this member to <see cref="SCRIPT_UNDEFINED"/>.</para>
		/// </summary>
		public ushort eScript { get => BitHelper.GetBits(bits, 0, 10); set => BitHelper.SetBits(ref bits, 0, 10, value); }

		/// <summary>
		/// <para>
		/// Value indicating rendering direction. Possible values are defined in the following table. This member is set to <c>TRUE</c>
		/// for a number in a left-to-right run, because digits are always displayed left to right, or <c>FALSE</c> for a number in a
		/// right-to-left run. The value of this member is normally identical to the parity of the Unicode embedding level, but it might
		/// differ if overridden by GetCharacterPlacement legacy support.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c><c>TRUE</c></c></term>
		/// <term>Use a right-to-left rendering direction.</term>
		/// </item>
		/// <item>
		/// <term><c><c>FALSE</c></c></term>
		/// <term>Use a left-to-right rendering direction.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fRTL { get => BitHelper.GetBit(bits, 10); set => BitHelper.SetBit(ref bits, 10, value); }

		/// <summary>
		/// <para>
		/// Value indicating layout direction for a number. Possible values are defined in the following table. This member is usually
		/// the same as the value assigned to <c>fRTL</c> for a number in a right-to-left run.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c><c>TRUE</c></c></term>
		/// <term>Lay out the number in a right-to-left run, because it is read as part of the right-to-left sequence.</term>
		/// </item>
		/// <item>
		/// <term><c><c>FALSE</c></c></term>
		/// <term>Lay out the number in a left-to-right run, because it is read as part of the left-to-right sequence.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fLayoutRTL { get => BitHelper.GetBit(bits, 11); set => BitHelper.SetBit(ref bits, 11, value); }

		/// <summary>
		/// <para>
		/// Value indicating if the shaping engine shapes the first character of the item as if it joins with a previous character.
		/// Possible values are defined in the following table. This member is set by ScriptItemize. The application can override the
		/// value before calling ScriptShape.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c><c>TRUE</c></c></term>
		/// <term>Shape the first character by linking with a previous character.</term>
		/// </item>
		/// <item>
		/// <term><c><c>FALSE</c></c></term>
		/// <term>Do not shape the first character by linking with a previous character.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fLinkBefore { get => BitHelper.GetBit(bits, 12); set => BitHelper.SetBit(ref bits, 12, value); }

		/// <summary>
		/// <para>
		/// Value indicating if the shaping engine shapes the last character of the item as if it joins with a subsequent character.
		/// Possible values are defined in the following table. This member is set by ScriptItemize. The application can override the
		/// value before calling <c>ScriptItemize</c>.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c><c>TRUE</c></c></term>
		/// <term>Shape the last character by linking with a subsequent character.</term>
		/// </item>
		/// <item>
		/// <term><c><c>FALSE</c></c></term>
		/// <term>Do not shape the last character by linking with a subsequent character.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fLinkAfter { get => BitHelper.GetBit(bits, 13); set => BitHelper.SetBit(ref bits, 13, value); }

		/// <summary>
		/// <para>
		/// Value indicating if the shaping engine generates all glyph-related arrays in logical order. Possible values are defined in
		/// the following table. This member is set to <c>FALSE</c> by ScriptItemize. The application can override the value before
		/// calling ScriptShape.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c><c>TRUE</c></c></term>
		/// <term>Generate all glyph-related arrays in logical order.</term>
		/// </item>
		/// <item>
		/// <term><c><c>FALSE</c></c></term>
		/// <term>
		/// Generate all glyph-related arrays in visual order, with the first array entry corresponding to the leftmost glyph. This value
		/// is the default.
		/// </term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fLogicalOrder { get => BitHelper.GetBit(bits, 14); set => BitHelper.SetBit(ref bits, 14, value); }

		/// <summary>
		/// <para>
		/// Value indicating the use of glyphs for the item. Possible values are defined in the following table. The application can set
		/// this member to <c>TRUE</c> on input to ScriptShape to disable the use of glyphs for the item. Additionally,
		/// <c>ScriptShape</c> sets it to <c>TRUE</c> for a hardware context containing symbolic, unrecognized, and device fonts.
		/// </para>
		/// <para>
		/// Disabling the use of glyphs also disables complex script shaping. Setting this member to <c>TRUE</c> implements shaping and
		/// placing directly by calls to GetTextExtentExPoint and ExtTextOut.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c><c>TRUE</c></c></term>
		/// <term>Disable the use of glyphs for the item. This value is used for bitmap, vector, and device fonts.</term>
		/// </item>
		/// <item>
		/// <term><c><c>FALSE</c></c></term>
		/// <term>Enable the use of glyphs for the item. This value is the default.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fNoGlyphIndex { get => BitHelper.GetBit(bits, 15); set => BitHelper.SetBit(ref bits, 15, value); }

		/// <summary>A SCRIPT_STATE structure containing a copy of the Unicode algorithm state.</summary>
		public SCRIPT_STATE s;
	}

	/// <summary>
	/// Contains information about a single character in a run (input string). The information indicates if the character glyph is
	/// affected by surrounding letters of the run.
	/// </summary>
	/// <remarks>
	/// <para>
	/// One or more characters in a run, immediately preceding and/or following the letter being shaped, can influence shaping.
	/// Information about these characters can help optimize higher-level layout code, such as that used to optimize paragraph layout.
	/// </para>
	/// <para>Examples</para>
	/// <para>Let's look at an example of the use of this structure.</para>
	/// <list type="bullet">
	/// <item>
	/// <term>A font has ligatures for letter combinations "fi" and "fl", and no others.</term>
	/// </item>
	/// <item>
	/// <term>The input string is "I like flying fish".</term>
	/// </item>
	/// <item>
	/// <term>An array of <c>SCRIPT_CHARPROP</c> structures contains one structure for each character of the input string.</term>
	/// </item>
	/// </list>
	/// <para>For the provided input string, the array of structures has the following values in the <c>fCanGlyphAlone</c> members:</para>
	/// <para>
	/// <code>I like flying fish 111111100111110011</code>
	/// </para>
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/usp10/ns-usp10-script_charprop typedef struct script_charprop { WORD
	// fCanGlyphAlone : 1; WORD reserved : 15; } SCRIPT_CHARPROP;
	[PInvokeData("usp10.h", MSDNShortId = "NS:usp10.script_charprop")]
	[StructLayout(LayoutKind.Sequential)]
	public struct SCRIPT_CHARPROP
	{
		private ushort bits;

		/// <summary>
		/// <para>
		/// Value indicating if the shaping of a letter depends on other characters around the letter being shaped. Possible values are
		/// defined in the following table.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>TRUE</term>
		/// <term>The shape of a letter is independent of surrounding characters.</term>
		/// </item>
		/// <item>
		/// <term>FALSE</term>
		/// <term>The shape of a letter depends on one or more adjacent characters.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fCanGlyphAlone { get => BitHelper.GetBit(bits, 0); set => BitHelper.SetBit(ref bits, 0, value); }

		/// <summary>Reserved.</summary>
		public ushort reserved { get => BitHelper.GetBits(bits, 1, 15); set => BitHelper.SetBits(ref bits, 1, 15, value); }
	}

	/// <summary>Contains script control flags for several Uniscribe functions, for example, ScriptItemize.</summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/usp10/ns-usp10-script_control typedef struct tag_SCRIPT_CONTROL { DWORD
	// uDefaultLanguage : 16; DWORD fContextDigits : 1; DWORD fInvertPreBoundDir : 1; DWORD fInvertPostBoundDir : 1; DWORD
	// fLinkStringBefore : 1; DWORD fLinkStringAfter : 1; DWORD fNeutralOverride : 1; DWORD fNumericOverride : 1; DWORD fLegacyBidiClass
	// : 1; DWORD fMergeNeutralItems : 1; DWORD fUseStandardBidi : 1; DWORD fReserved : 6; } SCRIPT_CONTROL;
	[PInvokeData("usp10.h", MSDNShortId = "NS:usp10.tag_SCRIPT_CONTROL")]
	[StructLayout(LayoutKind.Sequential)]
	public struct SCRIPT_CONTROL
	{
		/// <summary>
		/// Primary language identifier for the language to use when Unicode values are ambiguous. This value is used in numeric
		/// processing to select digit shape when the <c>fDigitSubstitute</c> member of SCRIPT_STATE is set.
		/// </summary>
		public ushort uDefaultLanguage;

		private ushort bits;

		/// <summary>
		/// <para>Value indicating how national digits are selected. Possible values are defined in the following table.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>Choose national digits according to the nearest previous strong text.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>Choose national digits according to the value of the <c>uDefaultLanguage</c> member.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fContextDigits { get => BitHelper.GetBit(bits, 0); set => BitHelper.SetBit(ref bits, 0, value); }

		/// <summary>
		/// <para>
		/// Value indicating if the initial context is set to the opposite of the base embedding level, or to the base embedding level
		/// itself. Possible values are defined in the following table. The application sets this member to indicate that text at the
		/// start of the string defaults to being laid out as if it follows a strong left-to-right character if the base embedding level
		/// is 0, and as if it follows a strong right-to-left character if the base embedding level is 1. This member is used for
		/// GetCharacterPlacement legacy support.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>Change the initial context to the opposite of the base embedding level.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>Set the initial context to the base embedding level.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fInvertPreBoundDir { get => BitHelper.GetBit(bits, 1); set => BitHelper.SetBit(ref bits, 1, value); }

		/// <summary>
		/// <para>
		/// Value indicating if the final context is set to the opposite of the base embedding level, or to the base embedding level
		/// itself. Possible values are defined in the following table. The application sets this member to indicate that text at the end
		/// of the string defaults to being laid out as if it precedes strong text of the same direction as the base embedding level. It
		/// is used for GetCharacterPlacement legacy support.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>Change the final context to the opposite of the base embedding level.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>Set the final context to the base embedding level.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fInvertPostBoundDir { get => BitHelper.GetBit(bits, 2); set => BitHelper.SetBit(ref bits, 2, value); }

		/// <summary>
		/// <para>
		/// Value indicating if the shaping engine shapes the first character of the string as if it joins with a previous character.
		/// Possible values are defined in the following table.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>Shape the first character by linking with a previous character.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>Do not shape the first character by linking with a previous character.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fLinkStringBefore { get => BitHelper.GetBit(bits, 3); set => BitHelper.SetBit(ref bits, 3, value); }

		/// <summary>
		/// <para>
		/// Value indicating if the shaping engine shapes the last character of the string as if it is joined to a subsequent character.
		/// Possible values are defined in the following table.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>Shape the last character by linking with a subsequent character.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>Do not shape the last character by linking with a subsequent character.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fLinkStringAfter { get => BitHelper.GetBit(bits, 4); set => BitHelper.SetBit(ref bits, 4, value); }

		/// <summary>
		/// <para>
		/// Value indicating the treatment of all neutral characters in the string. Possible values are defined in the following table.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>
		/// Set neutral items to a strong direction, that is, right-to-left or left-to-right, depending on the current embedding level.
		/// This setting effectively locks the items in place, and reordering occurs only between neutrals.
		/// </term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>Do not set neutral items to a strong direction.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fNeutralOverride { get => BitHelper.GetBit(bits, 5); set => BitHelper.SetBit(ref bits, 5, value); }

		/// <summary>
		/// <para>
		/// Value indicating the treatment of all numeric characters in the string. Possible values are defined in the following table.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>
		/// Set numeric characters to a strong direction, that is, right-to-left or left-to-right, depending on the current embedding
		/// level. This setting effectively locks the items in place, and reordering occurs only between numeric characters.
		/// </term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>Do not set numeric characters to a strong direction.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fNumericOverride { get => BitHelper.GetBit(bits, 6); set => BitHelper.SetBit(ref bits, 6, value); }

		/// <summary>
		/// <para>
		/// Value indicating the handling for plus and minus characters by the shaping engine. Possible values are defined in the
		/// following table.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>
		/// Treat the plus and minus characters as for legacy bidirectional classes in pre-Windows XP operating systems. In this case,
		/// the characters are treated as neutral characters, that is, with no implied direction, and the slash character is treated as a
		/// common separator.
		/// </term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>
		/// Treat the plus and minus characters as for Windows XP and later. In this case, the characters are treated as European separators.
		/// </term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fLegacyBidiClass { get => BitHelper.GetBit(bits, 7); set => BitHelper.SetBit(ref bits, 7, value); }

		/// <summary>
		/// <para>
		/// Value specifying if the shaping engine should merge neutral characters into strong items when possible. Possible values are
		/// defined in the following table.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>Merge neutral characters into strong items.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>Do not merge neutral characters into strong items.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fMergeNeutralItems { get => BitHelper.GetBit(bits, 8); set => BitHelper.SetBit(ref bits, 8, value); }

		/// <summary>
		/// <para>
		/// Value specifying if the shaping engine should use the standard bidirectional matching pair algorithm. Possible values are
		/// defined in the following table.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>Skip the matching pair algorithm.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>Use the matching pair algorithm.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fUseStandardBidi { get => BitHelper.GetBit(bits, 9); set => BitHelper.SetBit(ref bits, 9, value); }

		/// <summary>Reserved; always initialize to 0.</summary>
		public ushort fReserved { get => BitHelper.GetBits(bits, 10, 6); set => BitHelper.SetBits(ref bits, 10, 6, value); }
	}

	/// <summary>Contains native digit and digit substitution settings.</summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/usp10/ns-usp10-script_digitsubstitute typedef struct tag_SCRIPT_DIGITSUBSTITUTE
	// { DWORD NationalDigitLanguage : 16; DWORD TraditionalDigitLanguage : 16; DWORD DigitSubstitute : 8; DWORD dwReserved; } SCRIPT_DIGITSUBSTITUTE;
	[PInvokeData("usp10.h", MSDNShortId = "NS:usp10.tag_SCRIPT_DIGITSUBSTITUTE")]
	[StructLayout(LayoutKind.Sequential, Pack = 1)]
	public struct SCRIPT_DIGITSUBSTITUTE
	{
		/// <summary>Language for native substitution.</summary>
		public ushort NationalDigitLanguage;

		/// <summary>Language for traditional substitution.</summary>
		public ushort TraditionalDigitLanguage;

		/// <summary>
		/// <para>
		/// Substitution type. This member is normally set by ScriptRecordDigitSubstitution. However, it can also have any of the values
		/// defined in the following table.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>SCRIPT_DIGITSUBSTITUTE_CONTEXT</c></term>
		/// <term>
		/// Substitute digits U+0030 to U+0039 using the language of the prior letters. If there are no prior letters, substitute digits
		/// using the <c>TraditionalDigitLanguage</c> member. This member is normally set to the primary language of the locale passed to ScriptRecordDigitSubstitution.
		/// </term>
		/// </item>
		/// <item>
		/// <term><c>SCRIPT_DIGITSUBSTITUTE_NATIONAL</c></term>
		/// <term>
		/// Substitute digits U+0030 to U+0039 using the <c>NationalDigitLanguage</c> member. This member is normally set to the national
		/// digits retrieved for the constant LOCALE_SNATIVEDIGITS by ScriptRecordDigitSubstitution.
		/// </term>
		/// </item>
		/// <item>
		/// <term><c>SCRIPT_DIGITSUBSTITUTE_NONE</c></term>
		/// <term>Do not substitute digits. Display Unicode values U+0030 to U+0039 with European numerals.</term>
		/// </item>
		/// <item>
		/// <term><c>SCRIPT_DIGITSUBSTITUTE_TRADITIONAL</c></term>
		/// <term>
		/// Substitute digits U+0030 to U+0039 using the <c>TraditionalDigitLanguage</c> member. This member is normally set to the
		/// primary language of the locale passed to ScriptRecordDigitSubstitution.
		/// </term>
		/// </item>
		/// </list>
		/// </summary>
		public SCRIPT_DIGITSUB DigitSubstitute;

		/// <summary>Reserved; initialize to 0.</summary>
		public uint dwReserved;
	}

	/// <summary>Contains information about the properties of the current font.</summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/usp10/ns-usp10-script_fontproperties typedef struct { int cBytes; WORD wgBlank;
	// WORD wgDefault; WORD wgInvalid; WORD wgKashida; int iKashidaWidth; } SCRIPT_FONTPROPERTIES;
	[PInvokeData("usp10.h", MSDNShortId = "NS:usp10.__unnamed_struct_1")]
	[StructLayout(LayoutKind.Sequential)]
	public struct SCRIPT_FONTPROPERTIES
	{
		/// <summary>Size, in bytes, of the structure.</summary>
		public int cBytes;

		/// <summary>Glyph used to indicate a blank.</summary>
		public ushort wgBlank;

		/// <summary>Glyph used to indicate Unicode characters not present in the font.</summary>
		public ushort wgDefault;

		/// <summary>Glyph used to indicate invalid character combinations.</summary>
		public ushort wgInvalid;

		/// <summary>Glyph used to indicate the shortest continuous kashida, with 1 indicating that the font contains no kashida.</summary>
		public ushort wgKashida;

		/// <summary>Width of the shortest continuous kashida glyph in the font, indicated by the <c>wgKashida</c> member.</summary>
		public int iKashidaWidth;

		/// <summary>Gets a default instance with the size field set.</summary>
		public static SCRIPT_FONTPROPERTIES Default { get; } = new() { cBytes = Marshal.SizeOf(typeof(SCRIPT_FONTPROPERTIES)) };
	}

	/// <summary>Contains information about a glyph that is part of an output glyph array.</summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/usp10/ns-usp10-script_glyphprop typedef struct script_glyphprop {
	// SCRIPT_VISATTR sva; WORD reserved; } SCRIPT_GLYPHPROP;
	[PInvokeData("usp10.h", MSDNShortId = "NS:usp10.script_glyphprop")]
	[StructLayout(LayoutKind.Sequential)]
	public struct SCRIPT_GLYPHPROP
	{
		/// <summary>
		/// A buffer of SCRIPT_VISATTR structures defining visual (glyph) attributes identifying clusters and justification points. The
		/// buffer is generated by ScriptShape or ScriptShapeOpenType.
		/// </summary>
		public SCRIPT_VISATTR sva;

		/// <summary>Reserved.</summary>
		public ushort reserved;
	}

	/// <summary>
	/// Contains a script item, including a SCRIPT_ANALYSIS structure with the string offset of the first character of the item.
	/// </summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/usp10/ns-usp10-script_item typedef struct tag_SCRIPT_ITEM { int iCharPos;
	// SCRIPT_ANALYSIS a; } SCRIPT_ITEM;
	[PInvokeData("usp10.h", MSDNShortId = "NS:usp10.tag_SCRIPT_ITEM")]
	[StructLayout(LayoutKind.Sequential)]
	public struct SCRIPT_ITEM
	{
		/// <summary>
		/// Offset from the beginning of the itemized string to the first character of the item, counted in Unicode code points (WCHAR values).
		/// </summary>
		public int iCharPos;

		/// <summary>A SCRIPT_ANALYSIS structure containing the analysis of the item.</summary>
		public SCRIPT_ANALYSIS a;
	}

	/// <summary>Contains attributes of logical characters that are useful when editing and formatting text.</summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/usp10/ns-usp10-script_logattr typedef struct tag_SCRIPT_LOGATTR { BYTE
	// fSoftBreak : 1; BYTE fWhiteSpace : 1; BYTE fCharStop : 1; BYTE fWordStop : 1; BYTE fInvalid : 1; BYTE fReserved : 3; } SCRIPT_LOGATTR;
	[PInvokeData("usp10.h", MSDNShortId = "NS:usp10.tag_SCRIPT_LOGATTR")]
	[StructLayout(LayoutKind.Sequential)]
	public struct SCRIPT_LOGATTR
	{
		private byte bits;

		/// <summary>
		/// <para>
		/// Value indicating if breaking the line in front of the character, called a "soft break", is valid. Possible values are defined
		/// in the following table. This member is set on the first character of Southeast Asian words.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>A soft break is valid.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>A soft break is not valid.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fSoftBreak { get => BitHelper.GetBit(bits, 0); set => BitHelper.SetBit(ref bits, 0, value); }

		/// <summary>
		/// <para>
		/// Value indicating if the character is one of the many Unicode characters classified as breakable white space. Possible values
		/// are defined in the following table. Breakable white space can break a word. All white space is breakable except nonbreaking
		/// space (NBSP) and zero-width nonbreaking space (ZWNBSP).
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>The character is breakable white space.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>The character is not breakable white space.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fWhiteSpace { get => BitHelper.GetBit(bits, 1); set => BitHelper.SetBit(ref bits, 1, value); }

		/// <summary>
		/// <para>
		/// Value indicating if the character is a valid position for showing the caret upon a character movement keyboard action.
		/// Possible values are defined in the following table. This member is set for most characters, but not on code points inside
		/// Indian and Southeast Asian character clusters. This member can be used to implement LEFT ARROW and RIGHT ARROW operations in editors.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>The character is a valid position for showing the caret upon a character movement keyboard action.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>The character is not a valid position for showing the caret upon a character movement keyboard action.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fCharStop { get => BitHelper.GetBit(bits, 2); set => BitHelper.SetBit(ref bits, 2, value); }

		/// <summary>
		/// <para>
		/// Value indicating the valid position for showing the caret upon a word movement keyboard action, such as CTRL+LEFT ARROW and
		/// CTRL+RIGHT ARROW. Possible values are defined in the following table. This member can be used to implement the CTRL+LEFT
		/// ARROW and CTRL+RIGHT ARROW operations in editors.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>The character is a valid position for showing the caret upon a word movement keyboard action.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>The character is not a valid position for showing the caret upon a word movement keyboard action.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fWordStop { get => BitHelper.GetBit(bits, 3); set => BitHelper.SetBit(ref bits, 3, value); }

		/// <summary>
		/// <para>
		/// Value used to mark characters that form an invalid or undisplayable combination. Possible values are defined in the following
		/// table. A script that can set this member has the <c>fInvalidLogAttr</c> member set in its SCRIPT_PROPERTIES structure.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>The character forms an invalid or undisplayable combination.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>The character does not form an invalid or undisplayable combination.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fInvalid { get => BitHelper.GetBit(bits, 4); set => BitHelper.SetBit(ref bits, 4, value); }

		/// <summary>Reserved.</summary>
		public byte fReserved { get => BitHelper.GetBits(bits, 5, 3); set => BitHelper.SetBits(ref bits, 5, 3, value); }
	}

	/// <summary>Contains information about special processing for each script.</summary>
	/// <remarks>
	/// <para>This structure is filled by the ScriptGetProperties function.</para>
	/// <para>
	/// Many Uniscribe scripts do not correspond directly to 8-bit character sets. When some of the characters in a script are supported
	/// by more than one character set, the <c>fAmbiguousCharSet</c> member is set. The application should do further processing to
	/// determine the character set to use when requesting a font suitable for the run. For example, it might determine that the run
	/// consists of multiple languages and split the run so that a different font is used for each language.
	/// </para>
	/// <para>The application uses the following code during initialization to get a pointer to the <c>SCRIPT_PROPERTIES</c> array.</para>
	/// <para>
	/// <code>const SCRIPT_PROPERTIES **ppScriptProperties; // Array of pointers // to properties int iMaxScript; HRESULT hr; hr = ScriptGetProperties(&amp;ppScriptProperties, &amp;iMaxScript);</code>
	/// </para>
	/// <para>Then the application can inspect the properties of the script of an item as shown in the next example.</para>
	/// <para>
	/// <code>hr = ScriptItemize(pwcInChars, cInChars, cMaxItems, psControl, psState, pItems, pcItems); //... if (ppScriptProperties[pItems[iItem].a.eScript]-&gt;fNeedsCaretInfo) { // Use ScriptBreak to restrict the caret from entering clusters (for example). }</code>
	/// </para>
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/usp10/ns-usp10-script_properties typedef struct { DWORD langid : 16; DWORD
	// fNumeric : 1; DWORD fComplex : 1; DWORD fNeedsWordBreaking : 1; DWORD fNeedsCaretInfo : 1; DWORD bCharSet : 8; DWORD fControl : 1;
	// DWORD fPrivateUseArea : 1; DWORD fNeedsCharacterJustify : 1; DWORD fInvalidGlyph : 1; DWORD fInvalidLogAttr : 1; DWORD fCDM : 1;
	// DWORD fAmbiguousCharSet : 1; DWORD fClusterSizeVaries : 1; DWORD fRejectInvalid : 1; } SCRIPT_PROPERTIES;
	[PInvokeData("usp10.h", MSDNShortId = "NS:usp10.__unnamed_struct_0")]
	[StructLayout(LayoutKind.Sequential)]
	public struct SCRIPT_PROPERTIES
	{
		private ulong bits;

		/// <summary>
		/// Language identifier for the language associated with the script. When a script is used for many languages, this member
		/// represents a default language. For example, Western script is represented by LANG_ENGLISH although it is also used for
		/// French, German, and other European languages.
		/// </summary>
		public ushort langid { get => (ushort)BitHelper.GetBits(bits, 0, 16); set => BitHelper.SetBits(ref bits, 0, 16, value); }

		/// <summary>
		/// <para>
		/// Value indicating if a script contains only digits and the other characters used in writing numbers by the rules of the
		/// Unicode bidirectional algorithm. For example, currency symbols, the thousands separator, and the decimal point are classified
		/// as numeric when adjacent to or between digits. Possible values for this member are defined in the following table.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>
		/// The script contains only digits and the other characters used in writing numbers by the rules of the Unicode bidirectional algorithm.
		/// </term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>
		/// The script does not contain only digits and the other characters used in writing numbers by the rules of the Unicode
		/// bidirectional algorithm.
		/// </term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fNumeric { get => BitHelper.GetBit(bits, 16); set => BitHelper.SetBit(ref bits, 16, value); }

		/// <summary>
		/// <para>
		/// Value indicating a complex script for a language that requires special shaping or layout. Possible values are defined in the
		/// following table.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>The script requires special shaping or layout.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>The script contains no combining characters and requires no contextual shaping or reordering.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fComplex { get => BitHelper.GetBit(bits, 17); set => BitHelper.SetBit(ref bits, 17, value); }

		/// <summary>
		/// <para>Value indicating the type of word break placement for a language. Possible values are defined in the following table.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>
		/// The language has word break placement that requires the application to call ScriptBreak and that includes character positions
		/// marked by the <c>fWordStop</c> member in SCRIPT_LOGATTR.
		/// </term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>
		/// Word break placement is identified by scanning for characters marked by the <c>fWhiteSpace</c> member in SCRIPT_LOGATTR, or
		/// for glyphs marked by the value SCRIPT_JUSTIFY_BLANK or SCRIPT_JUSTIFY_ARABIC_BLANK for the <c>uJustification</c> member of SCRIPT_VISATTR.
		/// </term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fNeedsWordBreaking { get => BitHelper.GetBit(bits, 18); set => BitHelper.SetBit(ref bits, 18, value); }

		/// <summary>
		/// <para>
		/// Value indicating if a language, for example, Thai or Indian, restricts caret placement to cluster boundaries. Possible values
		/// are defined in the following table. To determine valid caret positions, the application inspects the <c>fCharStop</c> value
		/// in the logical attributes retrieved by ScriptBreak, or compares adjacent values in the <c>pwLogClust</c> array retrieved by ScriptShape.
		/// </para>
		/// <para><c>Note</c> ScriptXtoCP and ScriptCPtoX automatically apply caret placement restrictions.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>The language restricts caret placement to cluster boundaries.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>The language does not restrict caret placement to cluster boundaries.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fNeedsCaretInfo { get => BitHelper.GetBit(bits, 19); set => BitHelper.SetBit(ref bits, 19, value); }

		/// <summary>
		/// <para>
		/// Nominal character set associated with the script. During creation of a font suitable for displaying the script, this
		/// character set can be used as the value of the <c>lfCharSet</c> member of LOGFONT.
		/// </para>
		/// <para>
		/// For a new script having no character set defined, the application should typically set <c>bCharSet</c> to DEFAULT_CHARSET.
		/// See the description of member <c>fAmbiguousCharSet</c>.
		/// </para>
		/// </summary>
		public byte bCharSet { get => (byte)BitHelper.GetBits(bits, 20, 8); set => BitHelper.SetBits(ref bits, 20, 8, value); }

		/// <summary>
		/// <para>
		/// Value indicating if only control characters are used in the script. Possible values are defined in the following table. Note
		/// that every control character does not end up in a SCRIPT_CONTROL structure.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>Set only control characters in the script.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>Do not set only control characters in the script.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fControl { get => BitHelper.GetBit(bits, 28); set => BitHelper.SetBit(ref bits, 28, value); }

		/// <summary>
		/// <para>
		/// Value indicating the use of a private use area, a special set of characters that is privately defined for the Unicode range
		/// U+E000 through U+F8FF. Possible values are defined in the following table.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>Use a private use area.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>Do not use a private use area.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fPrivateUseArea { get => BitHelper.GetBit(bits, 29); set => BitHelper.SetBit(ref bits, 29, value); }

		/// <summary>
		/// <para>
		/// Value indicating the handling of justification for the script by increasing all the spaces between letters, not just the
		/// spaces between words. Possible values are defined in the following table. When performing inter-character justification,
		/// Uniscribe inserts extra space only after glyphs marked with the SCRIPT_JUSTIFY_CHARACTER value for the <c>uJustification</c>
		/// member of SCRIPT_VISATTR.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>Use character justification.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>Do not use character justification.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fNeedsCharacterJustify { get => BitHelper.GetBit(bits, 30); set => BitHelper.SetBit(ref bits, 30, value); }

		/// <summary>
		/// <para>
		/// Value indicating if ScriptShape generates an invalid glyph for a script to represent invalid sequences. Possible values are
		/// defined in the following table. The application can obtain the glyph index of the invalid glyph for a particular font by
		/// calling ScriptGetFontProperties.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>Generate an invalid glyph to represent invalid sequences.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>Do not generate an invalid glyph to represent invalid sequences.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fInvalidGlyph { get => BitHelper.GetBit(bits, 31); set => BitHelper.SetBit(ref bits, 31, value); }

		/// <summary>
		/// <para>
		/// Value indicating if ScriptBreak marks invalid combinations for a script by setting <c>fInvalid</c> in the logical attributes
		/// buffer. Possible values are defined in the following table.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>Mark invalid combinations for the script.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>Do not mark invalid combinations for the script.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fInvalidLogAttr { get => BitHelper.GetBit(bits, 32); set => BitHelper.SetBit(ref bits, 32, value); }

		/// <summary>
		/// <para>
		/// Value indicating if a script contains an item that has been analyzed by ScriptItemize as including Combining Diacritical
		/// Marks (U+0300 through U+36F). Possible values are defined in the following table.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>The script contains an item that includes combining diacritical marks.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>The script does not contain an item that includes combining diacritical marks.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fCDM { get => BitHelper.GetBit(bits, 33); set => BitHelper.SetBit(ref bits, 33, value); }

		/// <summary>
		/// <para>
		/// Value indicating if a script contains characters that are supported by more than one character set. Possible values are
		/// defined in the following table.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>
		/// The script contains characters that are supported by more than one character set. In this case, the <c>bCharSet</c> member of
		/// this structure should be ignored, and the <c>lfCharSet</c> member of LOGFONT should be set to DEFAULT_CHARSET. See the
		/// Remarks section for more information.
		/// </term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>The script does not contain characters that are supported by more than one character set.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fAmbiguousCharSet { get => BitHelper.GetBit(bits, 34); set => BitHelper.SetBit(ref bits, 34, value); }

		/// <summary>
		/// <para>
		/// Value indicating if a script, such as Arabic, might use contextual shaping that causes a string to increase in size during
		/// removal of characters. Possible values are defined in the following table.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>Use a variable cluster size for contextual shaping.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>Do not use a variable cluster size for contextual shaping.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fClusterSizeVaries { get => BitHelper.GetBit(bits, 35); set => BitHelper.SetBit(ref bits, 35, value); }

		/// <summary>
		/// <para>
		/// Value indicating if a script, for example, Thai, should reject invalid sequences that conventionally cause an editor program,
		/// such as Notepad, to beep and ignore keystrokes. Possible values are defined in the following table.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>Reject invalid sequences.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>Do not reject invalid sequences.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fRejectInvalid { get => BitHelper.GetBit(bits, 36); set => BitHelper.SetBit(ref bits, 36, value); }

		/// <summary>Performs an implicit conversion from <see cref="System.Int64"/> to <see cref="SCRIPT_PROPERTIES"/>.</summary>
		/// <param name="value">The value.</param>
		/// <returns>The result of the conversion.</returns>
		public static implicit operator SCRIPT_PROPERTIES(long value) => new() { bits = unchecked((ulong)value) };
	}

	/// <summary>Contains script state information.</summary>
	/// <remarks>
	/// This structure is used to initialize the Unicode algorithm state as an input to ScriptItemize. It is also used as a component of
	/// the analysis retrieved by <c>ScriptItemize</c>.
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/usp10/ns-usp10-script_state typedef struct tag_SCRIPT_STATE { WORD uBidiLevel :
	// 5; WORD fOverrideDirection { get => BitHelper.GetBit(bits, 5); set => BitHelper.SetBit(ref bits, 5, value); } WORD fInhibitSymSwap
	// { get => BitHelper.GetBit(bits, 5); set => BitHelper.SetBit(ref bits, 5, value); } WORD fCharShape { get => BitHelper.GetBit(bits,
	// 5); set => BitHelper.SetBit(ref bits, 5, value); } WORD fDigitSubstitute { get => BitHelper.GetBit(bits, 5); set =>
	// BitHelper.SetBit(ref bits, 5, value); } WORD fInhibitLigate { get => BitHelper.GetBit(bits, 5); set => BitHelper.SetBit(ref bits,
	// 5, value); } WORD fDisplayZWG { get => BitHelper.GetBit(bits, 5); set => BitHelper.SetBit(ref bits, 5, value); } WORD
	// fArabicNumContext { get => BitHelper.GetBit(bits, 5); set => BitHelper.SetBit(ref bits, 5, value); } WORD fGcpClusters { get =>
	// BitHelper.GetBit(bits, 5); set => BitHelper.SetBit(ref bits, 5, value); } WORD fReserved { get => BitHelper.GetBit(bits, 5); set
	// => BitHelper.SetBit(ref bits, 5, value); } WORD fEngineReserved : 2; } SCRIPT_STATE;
	[PInvokeData("usp10.h", MSDNShortId = "NS:usp10.tag_SCRIPT_STATE")]
	[StructLayout(LayoutKind.Sequential)]
	public struct SCRIPT_STATE
	{
		private ushort bits;

		/// <summary>
		/// Embedding level associated with all characters in the associated run according to the Unicode bidirectional algorithm. When
		/// the application passes this structure to ScriptItemize, this member should be initialized to 0 for a left-to-right base
		/// embedding level, or to 1 for a right-to-left base embedding level.
		/// </summary>
		public ushort uBidiLevel { get => BitHelper.GetBits(bits, 0, 5); set => BitHelper.SetBits(ref bits, 0, 5, value); }

		/// <summary>
		/// <para>
		/// Initial override direction value indicating if the script uses an override level (LRO or RLO code in the string). Possible
		/// values are defined in the following table. For an override level, characters are laid out in one direction only, either left
		/// to right or right to left. No reordering of digits or strong characters of opposing direction takes place. Note that this
		/// value is reset by LRE, RLE, LRO or RLO codes in the string.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c><c>TRUE</c></c></term>
		/// <term>Use an override level that reflects the embedding level.</term>
		/// </item>
		/// <item>
		/// <term><c><c>FALSE</c></c></term>
		/// <term>Do not use an override level that reflects the embedding level.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fOverrideDirection { get => BitHelper.GetBit(bits, 5); set => BitHelper.SetBit(ref bits, 5, value); }

		/// <summary>
		/// <para>
		/// Value indicating if the shaping engine bypasses mirroring of Unicode mirrored glyphs, for example, brackets. Possible values
		/// are defined in the following table. This member is set by Unicode character ISS, and cleared by ASS.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c><c>TRUE</c></c></term>
		/// <term>Bypass mirroring of Unicode mirrored glyphs.</term>
		/// </item>
		/// <item>
		/// <term><c><c>FALSE</c></c></term>
		/// <term>Do not bypass mirroring of Unicode mirrored glyphs.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fInhibitSymSwap { get => BitHelper.GetBit(bits, 6); set => BitHelper.SetBit(ref bits, 6, value); }

		/// <summary>
		/// <para>
		/// Not implemented. Value indicating if character codes in the Arabic Presentation Forms areas of Unicode should be shaped.
		/// Possible values are defined in the following table.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c><c>TRUE</c></c></term>
		/// <term>Shape character codes in the Arabic Presentation Forms areas of Unicode.</term>
		/// </item>
		/// <item>
		/// <term><c><c>FALSE</c></c></term>
		/// <term>Do not shape character codes in the Arabic Presentation Forms areas of Unicode.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fCharShape { get => BitHelper.GetBit(bits, 7); set => BitHelper.SetBit(ref bits, 7, value); }

		/// <summary>
		/// <para>
		/// This member provides the same control over digit substitution behavior that might have been obtained in legacy
		/// implementations using the now-deprecated Unicode characters U+206E NATIONAL DIGIT SHAPES ("NADS") and U+206F NOMINAL DIGIT
		/// SHAPES ("NODS"). Possible values are defined in the following table.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c><c>TRUE</c></c></term>
		/// <term>Character codes U+0030 through U+0039 are substituted by national digits.</term>
		/// </item>
		/// <item>
		/// <term><c><c>FALSE</c></c></term>
		/// <term>Character codes U+0030 through U+0039 are not substituted by national digits.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fDigitSubstitute { get => BitHelper.GetBit(bits, 8); set => BitHelper.SetBit(ref bits, 8, value); }

		/// <summary>
		/// <para>
		/// Value indicating if ligatures are used in the shaping of Arabic or Hebrew characters. Possible values are defined in the
		/// following table.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c><c>TRUE</c></c></term>
		/// <term>Do not use ligatures in the shaping of Arabic or Hebrew characters.</term>
		/// </item>
		/// <item>
		/// <term><c><c>FALSE</c></c></term>
		/// <term>Use ligatures in the shaping of Arabic or Hebrew characters.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fInhibitLigate { get => BitHelper.GetBit(bits, 9); set => BitHelper.SetBit(ref bits, 9, value); }

		/// <summary>
		/// <para>
		/// Value indicating if nondisplayable control characters are shaped as representational glyphs for languages that need
		/// reordering or different glyph shapes, depending on the positions of the characters within a word. Possible values are defined
		/// in the following table. Typically, the characters are not displayed. They are shaped to the blank glyph and given a width of 0.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c><c>TRUE</c></c></term>
		/// <term>Shape control characters as representational glyphs.</term>
		/// </item>
		/// <item>
		/// <term><c><c>FALSE</c></c></term>
		/// <term>Do not shape control characters as representational glyphs.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fDisplayZWG { get => BitHelper.GetBit(bits, 10); set => BitHelper.SetBit(ref bits, 10, value); }

		/// <summary>
		/// <para>
		/// Value indicating if prior strong characters are Arabic for the purposes of rule P0, as discussed in the Unicode Standard,
		/// version 2.0. Possible values are defined in the following table. This member should normally be set to <c>TRUE</c> before
		/// itemization of a right-to-left paragraph in an Arabic language, and to <c>FALSE</c> otherwise.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c><c>TRUE</c></c></term>
		/// <term>Consider prior strong characters to be Arabic for the purposes of rule P0.</term>
		/// </item>
		/// <item>
		/// <term><c><c>FALSE</c></c></term>
		/// <term>Do not consider prior strong characters to be Arabic for the purposes of rule P0.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fArabicNumContext { get => BitHelper.GetBit(bits, 11); set => BitHelper.SetBit(ref bits, 1, value); }

		/// <summary>
		/// <para>
		/// For GetCharacterPlacement legacy support only. Value indicating how ScriptShape should generate the array indicated by
		/// <c>pwLogClust</c>. Possible values are defined in the following table. This member affects only Arabic and Hebrew items.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c><c>TRUE</c></c></term>
		/// <term>Generate the array the same way as GetCharacterPlacement does.</term>
		/// </item>
		/// <item>
		/// <term><c><c>FALSE</c></c></term>
		/// <term>Do not generate the array the same way as GetCharacterPlacement does.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fGcpClusters { get => BitHelper.GetBit(bits, 12); set => BitHelper.SetBit(ref bits, 12, value); }

		/// <summary>Reserved; always initialize to 0.</summary>
		public bool fReserved { get => BitHelper.GetBit(bits, 13); set => BitHelper.SetBit(ref bits, 13, value); }

		/// <summary>Reserved; always initialize to 0.</summary>
		public ushort fEngineReserved { get => BitHelper.GetBits(bits, 14, 2); set => BitHelper.SetBits(ref bits, 14, 2, value); }
	}

	/// <summary>Contains definitions of the tab positions for ScriptStringAnalyse.</summary>
	/// <remarks>This structure is ignored unless the <c>dwFlags</c> parameter is set to SSA_TAB in the ScriptStringAnalyse function.</remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/usp10/ns-usp10-script_tabdef typedef struct tag_SCRIPT_TABDEF { int cTabStops;
	// int iScale; int *pTabStops; int iTabOrigin; } SCRIPT_TABDEF;
	[PInvokeData("usp10.h", MSDNShortId = "NS:usp10.tag_SCRIPT_TABDEF")]
	[StructLayout(LayoutKind.Sequential)]
	public struct SCRIPT_TABDEF
	{
		/// <summary>
		/// <para>Number of entries in the array indicated by <c>pTabStops</c>.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>0</term>
		/// <term>Tab stops occur every eight average character widths.</term>
		/// </item>
		/// <item>
		/// <term>1</term>
		/// <term>All tab stops are the length of the first entry in the array indicated by <c>pTabStops</c>.</term>
		/// </item>
		/// <item>
		/// <term>greater than 1</term>
		/// <term>
		/// The first <c>cTabStops</c> tab stops are as specified in the array indicated by <c>pTabStops</c>, and subsequent tab stops
		/// are every eight average characters.
		/// </term>
		/// </item>
		/// </list>
		/// </summary>
		public int cTabStops;

		/// <summary>
		/// Scale factor for <c>iTabOrigin</c> and <c>pTabStops</c> values. Values are converted to device coordinates by multiplying by
		/// the value indicated by <c>iScale</c>, then dividing by 4. If values are already in device units, set <c>iScale</c> to 4. If
		/// values are in dialog units, set <c>iScale</c> to the average character width of the dialog font. If values are multiples of
		/// the average character width for the selected font, set <c>iScale</c> to 0.
		/// </summary>
		public int iScale;

		/// <summary>
		/// Pointer to an array having the number of entries indicated by <c>cTabStops</c>. Each entry specifies a tab stop position.
		/// Positive values represent near-edge alignment, while negative values represent far-edge alignment. The units for the array
		/// elements are as indicated by the value of <c>iScale</c>.
		/// </summary>
		public IntPtr pTabStops;

		/// <summary>
		/// Initial offset, in logical units, for tab stops. Tabs start <c>iTabOrigin</c> logical units before the beginning of the
		/// string. This rule helps with situations in which multiple tabbed outputs occur on the same line.
		/// </summary>
		public int iTabOrigin;
	}

	/// <summary>Contains the visual (glyph) attributes that identify clusters and justification points, as generated by ScriptShape.</summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/usp10/ns-usp10-script_visattr typedef struct tag_SCRIPT_VISATTR { WORD
	// uJustification : 4; WORD fClusterStart { get => BitHelper.GetBit(bits, 5); set => BitHelper.SetBit(ref bits, 5, value); } WORD
	// fDiacritic { get => BitHelper.GetBit(bits, 5); set => BitHelper.SetBit(ref bits, 5, value); } WORD fZeroWidth { get =>
	// BitHelper.GetBit(bits, 5); set => BitHelper.SetBit(ref bits, 5, value); } WORD fReserved { get => BitHelper.GetBit(bits, 5); set
	// => BitHelper.SetBit(ref bits, 5, value); } WORD fShapeReserved : 8; } SCRIPT_VISATTR;
	[PInvokeData("usp10.h", MSDNShortId = "NS:usp10.tag_SCRIPT_VISATTR")]
	[StructLayout(LayoutKind.Sequential)]
	public struct SCRIPT_VISATTR
	{
		private ushort bits;

		/// <summary>Justification class for the glyph. See SCRIPT_JUSTIFY.</summary>
		public SCRIPT_JUSTIFY uJustification { get => (SCRIPT_JUSTIFY)BitHelper.GetBits(bits, 0, 4); set => BitHelper.SetBits(ref bits, 0, 4, (ushort)value); }

		/// <summary>
		/// <para>
		/// Value indicating the logical first glyph in every cluster, even for clusters containing just one glyph. Possible values are
		/// defined in the following table.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>The glyph is the logical first glyph of the cluster.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>The glyph is not the logical first glyph of the cluster.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fClusterStart { get => BitHelper.GetBit(bits, 4); set => BitHelper.SetBit(ref bits, 4, value); }

		/// <summary>
		/// <para>Value indicating if a glyph combines with base characters. Possible values are defined in the following table.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>The glyph does combine with base characters.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>The glyph does not combine with base characters.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fDiacritic { get => BitHelper.GetBit(bits, 5); set => BitHelper.SetBit(ref bits, 5, value); }

		/// <summary>
		/// <para>
		/// Value set by the shaping engine to indicate a zero-width character, such as ZWJ and ZWNJ. This value is set for some, but not
		/// all, zero-width characters. Possible values are defined in the following table.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>TRUE</c></term>
		/// <term>The glyph indicates a zero-width character.</term>
		/// </item>
		/// <item>
		/// <term><c>FALSE</c></term>
		/// <term>The glyph does not indicate a zero-width character.</term>
		/// </item>
		/// </list>
		/// </summary>
		public bool fZeroWidth { get => BitHelper.GetBit(bits, 6); set => BitHelper.SetBit(ref bits, 6, value); }

		/// <summary>Reserved; always initialize to 0.</summary>
		public bool fReserved { get => BitHelper.GetBit(bits, 7); set => BitHelper.SetBit(ref bits, 7, value); }

		/// <summary>Reserved; for use by shaping engines.</summary>
		public ushort fShapeReserved { get => BitHelper.GetBits(bits, 8, 8); set => BitHelper.SetBits(ref bits, 8, 8, value); }
	}

	/// <summary>Contains a group of OpenType features to apply to a run.</summary>
	// https://docs.microsoft.com/en-us/windows/win32/api/usp10/ns-usp10-textrange_properties typedef struct textrange_properties {
	// OPENTYPE_FEATURE_RECORD *potfRecords; int cotfRecords; } TEXTRANGE_PROPERTIES;
	[PInvokeData("usp10.h", MSDNShortId = "NS:usp10.textrange_properties")]
	[StructLayout(LayoutKind.Sequential)]
	public struct TEXTRANGE_PROPERTIES
	{
		/// <summary>
		/// Pointer to an array of OPENTYPE_FEATURE_RECORD structures containing OpenType features (records) to apply to the characters
		/// in a specific range of text in a run.
		/// </summary>
		public IntPtr potfRecords;

		/// <summary>Number of features in the array specified by <c>potfRecords</c>.</summary>
		public int cotfRecords;
	}

	/// <summary>
	/// Defines a Uniscribe font metric cache as a <see cref="SafeHandle"/> that is disposed using <see cref="ScriptFreeCache(ref IntPtr)"/>.
	/// </summary>
	/// <summary></summary>
	/// <remarks>
	/// <para>
	/// This is an opaque structure. The application must allocate and retain one SCRIPT_CACHE variable for each character style used.
	/// </para>
	/// <para>
	/// Many script functions take a combination of a hardware device context handle and a SCRIPT_CACHE variable. Uniscribe first
	/// attempts to access font data by using the SCRIPT_CACHE variable. It only inspects the hardware device context if the required
	/// data is not already cached.
	/// </para>
	/// <para>
	/// The hardware device context handle can be passed to Uniscribe as <c>NULL</c>. If data required by Uniscribe is already cached,
	/// the device context is not accessed, and the operation continues normally.
	/// </para>
	/// <para>
	/// If the device context is passed as <c>NULL</c> and Uniscribe needs to access it for any reason, Uniscribe returns the error code
	/// E_PENDING. This code is returned quickly, allowing the application to avoid time-consuming <c>SelectObject</c> calls.
	/// </para>
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/intl/script-cache
	[PInvokeData("usp10.h")]
	[StructLayout(LayoutKind.Sequential)]
	public class SafeSCRIPT_CACHE : IDisposable
	{
		private IntPtr handle = default;

		/// <summary>Initializes a new instance of the <see cref="SafeSCRIPT_CACHE"/> class.</summary>
		public SafeSCRIPT_CACHE()
		{ }

		/// <summary>Gets a value indicating whether this cache variable is invalid.</summary>
		/// <value><see langword="true"/> if this cache variable is invalid; otherwise, <see langword="false"/>.</value>
		public bool IsInvalid => handle == default;

		/// <summary>Releases unmanaged and - optionally - managed resources.</summary>
		public void Dispose() => ScriptFreeCache(ref handle);
	}

	/// <summary>Provides a <see cref="SafeHandle"/> for <c>SCRIPT_STRING_ANALYSIS</c> that is disposed using <see cref="ScriptStringFree"/>.</summary>
	[AutoSafeHandle("ScriptStringFree(ref handle).Succeeded")]
	public partial class SafeSCRIPT_STRING_ANALYSIS
	{
		private unsafe delegate TRet* GetVal<TRet>(SafeSCRIPT_STRING_ANALYSIS ssa) where TRet : unmanaged;

		/// <summary>Returns a logical attributes buffer for an analyzed string.</summary>
		/// <value>
		/// Returns a buffer containing SCRIPT_LOGATTR structures defining logical attributes if successful. The function returns <see
		/// langword="null"/> if it does not succeed.
		/// </value>
		/// <remarks>
		/// <para>The logical attribute buffer contains at least the number of integers indicated by the <c>ssa</c> parameter of ScriptString_pcOutChars.</para>
		/// <para>
		/// When scanning the SCRIPT_LOGATTR array for a word break point, the application should look backward for the values of the
		/// <c>fWordStop</c> and <c>fWhiteSpace</c> members. ScriptStringAnalyse just calls ScriptBreak on each run, and
		/// <c>ScriptBreak</c> never sets <c>fWordBreak</c> on the first character of a run, because it has no information that the
		/// previous run ended in white space.
		/// </para>
		/// </remarks>
		public SCRIPT_LOGATTR[] LogAttr
		{
			get
			{
				unsafe
				{
					SCRIPT_LOGATTR* a = ScriptString_pLogAttr(this);
					return a is not null ? ((IntPtr)a).ToArray<SCRIPT_LOGATTR>(OutChars.GetValueOrDefault())! : new SCRIPT_LOGATTR[0];
				}
			}
		}

		/// <summary>Converts visual widths into logical widths.</summary>
		/// <value>The logical widths.</value>
		/// <remarks>
		/// <para>
		/// This property converts the visual widths generated by ScriptStringAnalyse into logical widths, one per original character, in
		/// logical order.
		/// </para>
		/// <para>To use this property, the application needs to specify SSA_GLYPHS in its original call to ScriptStringAnalyse.</para>
		/// </remarks>
		public int[] LogicalWidths
		{
			get
			{
				int[] ret = new int[OutChars.GetValueOrDefault(0)];
				if (ret.Length == 0)
					return ret;
				ScriptStringGetLogicalWidths(this, ret).ThrowIfFailed();
				return ret;
			}
		}

		/// <summary>Gets an array that maps an original character position to a glyph position.</summary>
		/// <value>An array of glyph positions, indexed by the original character position.</value>
		/// <remarks>
		/// <para>
		/// When the number of glyphs and the number of characters are equal, the function retrieves an array that references every
		/// glyph. This is the same treatment that occurs in GetCharacterPlacement.
		/// </para>
		/// <para>To use this function, the application needs to specify SSA_GLYPHS in its original call to ScriptStringAnalyse.</para>
		/// </remarks>
		public uint[] Order
		{
			get
			{
				uint[] ret = new uint[OutChars.GetValueOrDefault(0)];
				if (ret.Length == 0)
					return ret;
				ScriptStringGetOrder(this, ret).ThrowIfFailed();
				return ret;
			}
		}

		/// <summary>Returns the length of a string after clipping.</summary>
		/// <value>
		/// Returns the length of the string after clipping if successful. The length is the number of Unicode code points. The function
		/// returns <see langword="null"/> if it does not succeed.
		/// </value>
		/// <remarks>To use this property, the application needs to specify SSA_CLIP in its original call to ScriptStringAnalyse.</remarks>
		public int? OutChars { get { unsafe { return RetOrDef<int>(ScriptString_pcOutChars); } } }

		/// <summary>Returns a SIZE structure for an analyzed string.</summary>
		/// <returns>
		/// Returns a pointer to a SIZE structure containing the size (width and height) of the analyzed string if successful. The
		/// function returns <see langword="null"/> if it does not succeed.
		/// </returns>
		/// <remarks>
		/// <para>
		/// The size returned by this function is the size before the effect of the justification requested by setting the SSA_FIT flag
		/// in ScriptStringAnalyse. The difference between the value of <c>iReqWidth</c> in <c>ScriptStringAnalyse</c> and the size
		/// returned by <c>ScriptString_pSize</c> is the effect of justification.
		/// </para>
		/// </remarks>
		public SIZE? Size { get { unsafe { return RetOrDef<SIZE>(ScriptString_pSize); } } }

		/// <summary>Retrieves the x coordinate for the leading or trailing edge of a character position.</summary>
		/// <param name="cp">Character position in the string.</param>
		/// <param name="trailing">
		/// <see langword="true"/> to indicate the trailing edge of the character position ( <c>icp</c>) that corresponds to the x
		/// coordinate. This parameter is set to <see langword="false"/> to indicate the leading edge of the character position.
		/// </param>
		/// <returns>The x coordinate corresponding to the character position.</returns>
		public int CPtoX(int cp, bool trailing)
		{
			ScriptStringCPtoX(this, cp, trailing, out int ret).ThrowIfFailed();
			return ret;
		}

		/// <summary>Displays a string generated by a prior call to ScriptStringAnalyse and optionally adds highlighting.</summary>
		/// <param name="pt">The x and y-coordinates of the reference point used to position the string.</param>
		/// <param name="options">
		/// <para>
		/// Options specifying the use of the application-defined rectangle. This parameter can be set to 0 or to any of the following
		/// values. The values can be combined with binary OR.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term><c>ETO_CLIPPED</c></term>
		/// <term>Clip text to the rectangle.</term>
		/// </item>
		/// <item>
		/// <term><c>ETO_OPAQUE</c></term>
		/// <term>Use current background color to fill the rectangle.</term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="rc">
		/// Pointer to a RECT structure that defines the rectangle to use. If <c>uOptions</c> is set to ETO_OPAQUE and <c>NULL</c> is
		/// provided for <c>prc</c>, the function succeeds and returns S_OK. However, if the application sets <c>uOptions</c> to ETO_CLIPPING
		/// and provides <c>NULL</c> for <c>prc</c>, the function returns E_INVALIDARG. The application can set this parameter to <c>NULL</c>
		/// to indicate that no option is needed.
		/// </param>
		/// <param name="minSel">
		/// Zero-based index specifying the starting position in the string. For no selection, the application should set <c>iMinSel &gt;= iMaxSel</c>.
		/// </param>
		/// <param name="maxSel">Zero-based index specifying the ending position in the string.</param>
		/// <param name="disabled">
		/// <c>TRUE</c> if the operating system applies disabled-text highlighting by setting the background color to COLOR_HIGHLIGHT behind
		/// all selected characters. The application can set this parameter to <c>FALSE</c> if the operating system applies enabled-text
		/// highlighting by setting the background color to COLOR_HIGHLIGHT and the text color to COLOR_HIGHLIGHTTEXT for each selected character.
		/// </param>
		/// <returns>
		/// Returns S_OK if successful. The function returns a nonzero <c>HRESULT</c> value if it does not succeed. The application can't
		/// test the return value with the <c>SUCCEEDED</c> and <c>FAILED</c> macros.
		/// </returns>
		/// <remarks>
		/// <para>To use this function, the application needs to specify SSA_GLYPHS in its original call to ScriptStringAnalyse.</para>
		/// <para>
		/// The application should not use SetTextAlign with TA_UPDATECP when using <c>ScriptStringOut</c> because selected text cannot be
		/// rendered correctly. If the application must use this flag, it can unset and reset the flag as necessary to avoid the problem.
		/// </para>
		/// </remarks>
		public HRESULT Out(POINT pt, Gdi32.ETO options = 0, [In] PRECT? rc = null, int minSel = 0, int maxSel = 0, [MarshalAs(UnmanagedType.Bool)] bool disabled = false) =>
			ScriptStringOut(this, pt.X, pt.Y, options, rc, minSel, maxSel, disabled);

		/// <summary>Checks for invalid sequences.</summary>
		/// <returns>
		/// Returns S_OK if no invalid sequences are found. The function returns S_FALSE if one or more invalid sequences are found. The
		/// function returns a nonzero HRESULT value if it does not succeed.
		/// </returns>
		/// <remarks>
		/// <para>This function is intended for use in editors that reject the input of invalid sequences.</para>
		/// <para>
		/// Invalid sequences are only checked for scripts with the <c>fRejectInvalid</c> member set in the associated SCRIPT_PROPERTIES
		/// structure. For example, it is conventional for Notepad to reject invalid Thai character sequences. However, invalid Indian
		/// sequences are not conventionally rejected, but instead are displayed in composition with a missing base character symbol.
		/// </para>
		/// </remarks>
		public HRESULT Validate() => ScriptStringValidate(this);

		/// <summary>Converts an x coordinate to a character position.</summary>
		/// <param name="x">The x coordinate.</param>
		/// <returns>
		/// A tuple with the character position in the string (cp) and a value indicating if the x coordinate is for the leading edge or
		/// the trailing edge of the character position (trailing). For more information, see the Remarks section.
		/// </returns>
		/// <remarks>
		/// <para>
		/// If the x coordinate corresponds to the leading edge of the character, the value of <c>piTrailing</c> is 0. If the x
		/// coordinate corresponds to the trailing edge of the character, the value of <c>piTrailing</c> is a positive integer. As for
		/// ScriptXtoCP, the value is 1 for a character that can be rendered on its own. The value is greater than 1 if the character is
		/// part of a cluster in a script for which cursors are not placed within a cluster, to indicate the offset to the next
		/// legitimate logical cursor position.
		/// </para>
		/// <para>
		/// If the x coordinate is before the beginning of the line, the function retrieves -1 for <c>piCh</c> and 1 for
		/// <c>piTrailing</c>, indicating the trailing edge of the nonexistent character before the line. If the x coordinate is after
		/// the end of the line, the function retrieves for <c>piCh</c> the first index beyond the length of the line and 0 for
		/// <c>piTrailing</c>. The 0 value indicates the leading edge of the nonexistent character after the line.
		/// </para>
		/// </remarks>
		public (int cp, int trailing) XtoCP(int x)
		{
			(int cp, int trailing) ret = default;
			ScriptStringXtoCP(this, x, out ret.cp, out ret.trailing).ThrowIfFailed();
			return ret;
		}

		private unsafe TRet? RetOrDef<TRet>(GetVal<TRet> f) where TRet : unmanaged
		{
			TRet* ret = f(this);
			return ret == null ? null : *ret;
		}
	}
}